<script setup lang="ts">
import type { SubMenuProps } from './types'
import { useTimeoutFn } from '@vueuse/core'
import { cn } from '@/utils'
import Item from './item.vue'
import { rootMenuInjectionKey } from './types'

defineOptions({
  name: 'SubMenu',
})

const props = withDefaults(
  defineProps<SubMenuProps>(),
  {
    level: 0,
  },
)

const itemRef = useTemplateRef('itemRef')
const subMenuRef = useTemplateRef('subMenuRef')
const rootMenu = inject(rootMenuInjectionKey)!

const index = props.menu.path ?? rootMenu.getUseId(props.menu)

const opened = computed(() => rootMenu.openedMenus.includes(props.uniqueKey.at(-1)!))

const transitionEvent = computed(() => {
  if (rootMenu.isMenuPopup) {
    return {
      enter(el: HTMLElement) {
        if (el.offsetHeight > window.innerHeight) {
          el.style.height = `${window.innerHeight}px`
        }
      },
      afterEnter: () => {},
      beforeLeave: (el: HTMLElement) => {
        el.style.maxHeight = `${el.offsetHeight}px`
        el.style.overflow = 'hidden'
      },
      leave: (el: HTMLElement) => {
        el.style.maxHeight = '0'
      },
      afterLeave(el: HTMLElement) {
        el.style.maxHeight = ''
        el.style.overflow = ''
      },
    }
  }
  if (CSS.supports('height', 'calc-size(auto, size)')) {
    return {}
  }
  return {
    enter(el: HTMLElement) {
      requestAnimationFrame(() => {
        el.dataset.height = el.offsetHeight.toString()
        el.style.maxHeight = '0'
        void el.offsetHeight
        el.style.maxHeight = `${el.dataset.height}px`
        el.style.overflow = 'hidden'
      })
    },
    afterEnter(el: HTMLElement) {
      el.style.maxHeight = ''
      el.style.overflow = ''
    },
    enterCancelled(el: HTMLElement) {
      el.style.maxHeight = ''
      el.style.overflow = ''
    },
    beforeLeave(el: HTMLElement) {
      el.style.maxHeight = `${el.offsetHeight}px`
      el.style.overflow = 'hidden'
    },
    leave(el: HTMLElement) {
      el.style.maxHeight = '0'
    },
    afterLeave(el: HTMLElement) {
      el.style.maxHeight = ''
      el.style.overflow = ''
    },
    leaveCancelled(el: HTMLElement) {
      el.style.maxHeight = ''
      el.style.overflow = ''
    },
  }
})

const transitionClass = computed(() => {
  if (rootMenu.isMenuPopup) {
    return {
      enterActiveClass: 'ease-in-out duration-300',
      enterFromClass: 'opacity-0 translate-x-4',
      enterToClass: 'opacity-100',
      leaveActiveClass: 'ease-in-out duration-300',
      leaveFromClass: 'opacity-100',
      leaveToClass: 'opacity-0',
    }
  }
  return {
    enterActiveClass: 'ease-in-out duration-300',
    enterFromClass: cn('opacity-0 translate-y-4 scale-95 blur-4', CSS.supports('height', 'calc-size(auto, size)') && 'h-0'),
    enterToClass: 'opacity-100 translate-y-0 scale-100 blur-0',
    leaveActiveClass: 'ease-in-out duration-300',
    leaveFromClass: 'opacity-100 translate-y-0 scale-100 blur-0',
    leaveToClass: cn('opacity-0 translate-y-4 scale-95 blur-4', CSS.supports('height', 'calc-size(auto, size)') && 'h-0'),
  }
})

const hasChildren = computed(() => props.menu.children?.some((item: any) => item.meta?.menu !== false) ?? false)

function handleClick() {
  if (
    (rootMenu.isMenuPopup && hasChildren.value)
    || props.menu.meta?.link
  ) {
    return
  }
  requestAnimationFrame(() => {
    if (hasChildren.value) {
      rootMenu.handleSubMenuClick(index, props.uniqueKey)
    }
    else {
      rootMenu.handleMenuItemClick(index)
    }
  })
}

let timeout: (() => void) | undefined

function handleMouseenter() {
  if (!rootMenu.isMenuPopup) {
    return
  }
  rootMenu.mouseInMenu = props.uniqueKey
  timeout?.()
  ;({ stop: timeout } = useTimeoutFn(() => {
    if (hasChildren.value) {
      rootMenu.openMenu(index, props.uniqueKey)
      nextTick(() => {
        requestAnimationFrame(() => {
          const el = itemRef.value?.ref
          const subMenuEl = subMenuRef.value?.$el
          if (!el || !subMenuEl) {
            return
          }
          const rect = el.getBoundingClientRect()
          const { top, left, width, height } = rect
          let menuTop = 0
          let menuLeft = 0
          if (rootMenu.props.mode === 'vertical' || props.level !== 0) {
            menuTop = top + el.scrollTop
            menuLeft = left + width
            // 处理边界情况
            if (menuTop + subMenuEl.offsetHeight > window.innerHeight) {
              menuTop = Math.max(0, window.innerHeight - subMenuEl.offsetHeight)
            }
          }
          else {
            menuTop = top + height
            menuLeft = left
            // 处理边界情况
            if (menuTop + subMenuEl.offsetHeight > window.innerHeight) {
              subMenuEl.style.height = `${window.innerHeight - menuTop}px`
            }
          }
          // 处理边界情况
          if (menuLeft + subMenuEl.offsetWidth > document.documentElement.clientWidth) {
            menuLeft = left - width
          }
          // 设置样式
          Object.assign(subMenuEl.style, {
            top: `${menuTop}px`,
            insetInlineStart: `${menuLeft}px`,
            willChange: 'transform',
            transform: 'translateZ(0)',
          })
        })
      })
    }
    else {
      const path = props.menu.children ? rootMenu.subMenus[index].indexPath.at(-1)! : rootMenu.items[index].indexPath.at(-1)!
      rootMenu.openMenu(path, rootMenu.subMenus[path].indexPath)
    }
  }, 300))
}

function handleMouseleave() {
  if (!rootMenu.isMenuPopup) {
    return
  }
  rootMenu.mouseInMenu = []
  timeout?.()
  ;({ stop: timeout } = useTimeoutFn(() => {
    requestAnimationFrame(() => {
      if (rootMenu.mouseInMenu.length === 0) {
        rootMenu.closeMenu(props.uniqueKey)
      }
      else {
        if (hasChildren.value) {
          !rootMenu.mouseInMenu.includes(props.uniqueKey.at(-1)!) && rootMenu.closeMenu(props.uniqueKey.at(-1)!)
        }
      }
    })
  }, 300))
}
</script>

<template>
  <Item ref="itemRef" :unique-key="uniqueKey" :item="menu" :level="level" :sub-menu="hasChildren" :expand="opened" @click="handleClick" @mouseenter="handleMouseenter" @mouseleave="handleMouseleave" />
  <Teleport v-if="hasChildren" to="body" :disabled="!rootMenu.isMenuPopup">
    <Transition v-bind="transitionClass" v-on="transitionEvent">
      <FaScrollArea
        v-if="opened" ref="subMenuRef" :scrollbar="false" :mask="rootMenu.isMenuPopup" :class="cn('sub-menu static h-[calc-size(auto,size)] rounded-lg will-change-transform', {
          'bg-[var(--g-sub-sidebar-bg)]': rootMenu.isMenuPopup,
          'border shadow-xl fixed z-3000 w-[200px]': rootMenu.isMenuPopup,
          'mx-1': rootMenu.isMenuPopup && (rootMenu.props.mode === 'vertical' || level !== 0),
          'py-1': rootMenu.isMenuPopup,
        })"
      >
        <template v-for="item in menu.children" :key="item.path ?? rootMenu.getUseId(item)">
          <SubMenu v-if="item.meta?.menu !== false" :unique-key="[...uniqueKey, item.path ?? rootMenu.getUseId(item)]" :menu="item" :level="level + 1" />
        </template>
      </FaScrollArea>
    </Transition>
  </Teleport>
</template>
